package com.springboot.limitSpring.dome;

import com.springboot.limitSpring.model.LimitRateResult;
import com.springboot.limitSpring.utils.JedisUtil;
import com.springboot.limitSpring.utils.PropertyUtil;

public class RateLimiter {

    protected String DEFAULT_LIMIT_LOCK_KEY = "LIMIT_LOCK_KEY";
    protected String DEFAULT_LIMIT_LOCK_VALUE = "LIMIT_LOCK_VALUE";
    //每一次向令牌桶里放置令牌数量
    protected String DEFAULT_PUT_TOKEN_COUNT = PropertyUtil.getProperty("redis.token.putcount");
    //每一次从令牌桶里面取得令牌数量
    protected int DEFAULT_GET_TOKEN_COUNT = Integer.valueOf(PropertyUtil.getProperty("redis.token.getcount"));
    //实例剩余token数量
    protected int leftToken;

    private static RateLimiter instance = null;
    JedisUtil jedisUtil = JedisUtil.getInstance();

    private RateLimiter() {
        leftToken = 0;

    }

    public static RateLimiter getInstance() {
        if (instance == null) {
            synchronized (RateLimiter.class) {
                if (instance == null) {
                    instance = new RateLimiter();
                }
            }
        }
        return instance;

    }

    /**
     * 从令牌桶里面取令牌，每次取100个，用完再取
     */
    public LimitRateResult acquire() {
        LimitRateResult limitRateResult = null;
        synchronized (RateLimiter.class) {
            if (leftToken <= 0) {
                if (jedisUtil.getString("LimitCount") == "No reachable node in cluster") {
                    limitRateResult = new LimitRateResult(-103, "redis访问失败，redis连接失败");
                } else if (jedisUtil.getString("LimitCount") == null || jedisUtil.ttl("LimitCount") <= 0) {
                    if (!supplement()) {
                        return new LimitRateResult(-101, "redis访问失败，获取lock失败");
                    } else {
                        limitRateResult = getToken();
                    }
                } else {
                    limitRateResult = getToken();
                }
            } else {
                limitRateResult = new LimitRateResult(200, "访问成功");
                leftToken--;

            }
        }

        return limitRateResult;
    }

    public LimitRateResult getToken() {
        if (jedisUtil.tryLock(DEFAULT_LIMIT_LOCK_KEY, DEFAULT_LIMIT_LOCK_VALUE, 1000 * 10, 1000 * 10)) {
            String count = jedisUtil.getString("LimitCount");
            if (count == "No reachable node in cluster") {
                return new LimitRateResult(-103, "redis访问失败，redis连接失败");
            }
            Integer countExist = (Integer) Integer.valueOf(count);
            System.out.println("countExist:" + countExist);
            if (countExist <= 0) {
                jedisUtil.tryUnLock(DEFAULT_LIMIT_LOCK_KEY, DEFAULT_LIMIT_LOCK_VALUE);
                return new LimitRateResult(-102, "LimitCount每分钟超过60000次访问，返回错误");
            } else {
                jedisUtil.decrBy("LimitCount", DEFAULT_GET_TOKEN_COUNT);
                leftToken = DEFAULT_GET_TOKEN_COUNT;
                jedisUtil.tryUnLock(DEFAULT_LIMIT_LOCK_KEY, DEFAULT_LIMIT_LOCK_VALUE);
                return new LimitRateResult(200, "访问成功");
            }
        }
        return new LimitRateResult(-101, "redis访问失败，获取lock失败");
    }

    /**
     * 利用redis失效，定期向令牌桶里面放置令牌
     */
    public boolean supplement() {
        //判断是否失效，等到失效重置
        if (jedisUtil.tryLock(DEFAULT_LIMIT_LOCK_KEY, DEFAULT_LIMIT_LOCK_VALUE, 1000 * 10, 1000 * 10)) {
            jedisUtil.remove("LimitCount");
            boolean res = jedisUtil.putString("LimitCount", DEFAULT_PUT_TOKEN_COUNT, 60);
            jedisUtil.tryUnLock(DEFAULT_LIMIT_LOCK_KEY, DEFAULT_LIMIT_LOCK_VALUE);
            return res;
        }
        return false;
    }

}